home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
CD ROM Paradise Collection 4
/
CD ROM Paradise Collection 4 1995 Nov.iso
/
hamradio
/
dspmorse.zip
/
MORSE.C
< prev
next >
Wrap
C/C++ Source or Header
|
1993-11-29
|
35KB
|
1,255 lines
/////////////////////////////////////////////////////////////////////////////
// MORSE.C
// Derived from FFTMORSE.C, Copyright (c) 1992 by François Jalbert.
// What's new:
// o Sound Blaster support is now DMA driven, up to 44000 bytes
// per second. Fast machines will lose little or no data.
// o Enhanced signal graphing, including color and a max-signal
// indicator.
// o Audio output when the signal meets the mark threshhold.
// Very handy for fine-tuning reception.
// o More FFT-to-tone and tone-to-morse parameters, adjustable
// while running.
// The FFT routine is nearly unchanged from François Jalbert's wonderful
// integer FFT. Everything else has evolved from ideas in FFTMORSE,
// also by Mr. Jalbert. Thank him for me if you see him.
//
// New bits by Rocco Caputo.
/////////////////////////////////////////////////////////////////////////////
#include "morse.h"
/////////////////////////////////////////////////////////////////////////////
static unsigned int sbSampleRate = SAMPLEMAX;
static unsigned int sbOver = 0;
static char noise=FALSE; // audio output toggle
static char done=FALSE; // program termination flag
static char autorate=TRUE; // automatic sample rate negociation
static char notTotReset=FALSE; // flag if it's *NOT* a totFFT reset
static char tuneout=FALSE; // tune-out flag
static char pending=FALSE; // flag for dots/dashes pending
static char maxlevel[N]; // maximum levels for the max display
static char fft[MAXFFT][N]; // scaled FFT buffer for smoothing
static char tonedetected=FALSE; // tone-detection indicator
static char oldtonedetected=FALSE; // tone previously detected
static SCHAR offset=0; // threshhold offset
static ULONG totMark, totSpace; // total mark and space buffers
static UINT avgMark, avgSpace; // average mark and space buffers
static UINT useMark=4, useSpace=4; // values to use for mark & space
static UINT thisCnt=0; // current stats
static UINT bufMark[MAXBUF]; // mark buffer for averages
static UINT bufSpace[MAXBUF]; // space buffer for averages
static char cntBuf=32; // mark/space buffer count
static char notMarkReset=FALSE; // flag to signal mark buffer reset
static char notSpaceReset=FALSE;// flag to signal space buffer reset
static UINT thrNoise=2; // runs under this are ignored
static char waitfactor=16; // use every Nth byte of input
static int dspcount; // count of bufdsp bytes analyzed
static int freqm2,freqm1, // signal window into FFT results
freqp2,freqp1,freq=(N>>1);
static int outm1,outp1, // frequency range to tune out
out=1;
static int code=0; // current morse code being built
static int mask=1; // mask to build morse code with
static UINT sbStat; // Becomes 0 when the next block of
// audio is ready for analysis.
char far *bufone, far *buftwo; // generic buffers
char far *bufin, far *bufdsp; // input and analysys buffers
UINT totFFT[N-1]; // total of the smoothed FFT thang
char oldFFT[N-1]; // last smoothed FFT thing
char avgFFT[N-1]; // average FFT over cntFFT iterations
char cntFFT=1; // number of FFT thangs to track
/////////////////////////////////////////////////////////////////////////////
//
// Calculate integer FFT on a stream of data.
int FFT(char *dsp)
{
// The number of bytes used for the
// transform, returned to the calling
// function so it knows when to go
// and fetch more data.
int samped;
int cntFFTm1;
// François Jalbert's FFT variables.
static int k,l,f[N+N];
static int level,oldlevel,line,column;
static int f16p0,f17m1,f17p1,f18m2,f18p2,f19m3,f19p3;
static int f20m4,f20p4,f21m5,f21p5,f22m6,f22p6,f23m7,f23p7;
static int f24p8,f25m9,f25p9,f26m10,f26p10,f27m11,f27p11;
static int f28m12,f28p12,f29m13,f29p13,f30m14,f30p14,f31m15,f31p15;
static int f25m9Mf23m7,f25m9Pf23m7,f26m10Mf22m6,f26m10Pf22m6;
static int f27m11Pf21m5,f29m13Mf19m3,f29m13Pf19m3,f27m11Mf21m5;
static int f30m14Mf18m2,f30m14Pf18m2,f31m15Mf17m1,f31m15Pf17m1;
static int f24p8Pf16p0,f25p9Mf23p7,f25p9Pf23p7,f26p10Mf22p6,f26p10Pf22p6;
static int f27p11Mf21p5,f27p11Pf21p5,f28p12Pf20p4,f29p13Mf19p3;
static int f29p13Pf19p3,f30p14Mf18p2,f30p14Pf18p2,f31p15Mf17p1,f31p15Pf17p1;
static int f29Mf19Pf27Mf21,f29Pf19Mf27Pf21,f29Mf19Mf27Mf21;
static int f31Mf17Pf25Mf23,f31Pf17Mf25Pf23,f31Mf17Mf25Mf23;
static int s4Mf28f20,s4Pf28f20;
static int s4Mf30f18f26f22,s4Pf30f26f22f18,s4Mf31f29f27f25,s4Pf31f29f27f25;
static int s8Mf30f18f26f22,s8Pf28f24f20f16,s8Mf24f16,s8Mf28f20,s8f16,s8f24;
static int s4Mf30f26Ms8Mf28,s4Mf30f26Ps8Mf28;
static int s4Pf30f22Ms8Mf24,s4Pf30f22Ps8Mf24;
static int s6Mf31f25Ms2Mf29f27,s6Mf29f27Ps2Mf31f25;
static int s6Pf29f27Ms2Pf31f25,s6Pf31f25Ps2Pf29f27;
static int s1Mf25s3f27s5f29s7f31,s1Pf25s3f27s5f29s7f31;
static int s1Mf27s3f31s5f25s7f29,s1Pf27s3f31s5f25s7f29;
static int s1Mf29s3f25s5f31s7f27,s1Pf29s3f25s5f31s7f27;
static int s1Mf31s3f29s5f27s7f25,s1Pf31s3f29s5f27s7f25;
static int s2Mf26Ps6Mf30,s2Pf26Ms6Pf30,s2Mf30Ms6Mf26,s2Pf30Ps6Pf26;
static int s4Mf28Ps8f16,s4Mf28Ms8f16,s4Pf28Ps8f24,s4Pf28Ms8f24;
static int s2Mf26Ps4f28s6f30Ms8f16,s2Mf26Ms4f28s6f30Ps8f16;
static int s2Pf26Ms4f28s6f30Ps8f24,s2Pf26Ps4f28s6f30Ms8f24;
static int s2Mf30Ps4f28s6f26Ps8f16,s2Mf30Ms4f28s6f26Ms8f16;
static int s2Pf30Ps4f28s6f26Ps8f24,s2Pf30Ms4f28s6f26Ms8f24;
// Fill the FFT matrix with samples
// from the input stream.
f[ 0]=(*(dsp+=waitfactor))-128;
f[ 1]=(*(dsp+=waitfactor))-128;
f[ 2]=(*(dsp+=waitfactor))-128;
f[ 3]=(*(dsp+=waitfactor))-128;
f[ 4]=(*(dsp+=waitfactor))-128;
f[ 5]=(*(dsp+=waitfactor))-128;
f[ 6]=(*(dsp+=waitfactor))-128;
f[ 7]=(*(dsp+=waitfactor))-128;
f[ 8]=(*(dsp+=waitfactor))-128;
f[ 9]=(*(dsp+=waitfactor))-128;
f[10]=(*(dsp+=waitfactor))-128;
f[11]=(*(dsp+=waitfactor))-128;
f[12]=(*(dsp+=waitfactor))-128;
f[13]=(*(dsp+=waitfactor))-128;
f[14]=(*(dsp+=waitfactor))-128;
f[15]=(*(dsp+=waitfactor))-128;
f[16]=(*(dsp+=waitfactor))-128;
f[17]=(*(dsp+=waitfactor))-128;
f[18]=(*(dsp+=waitfactor))-128;
f[19]=(*(dsp+=waitfactor))-128;
f[20]=(*(dsp+=waitfactor))-128;
f[21]=(*(dsp+=waitfactor))-128;
f[22]=(*(dsp+=waitfactor))-128;
f[23]=(*(dsp+=waitfactor))-128;
f[24]=(*(dsp+=waitfactor))-128;
f[25]=(*(dsp+=waitfactor))-128;
f[26]=(*(dsp+=waitfactor))-128;
f[27]=(*(dsp+=waitfactor))-128;
f[28]=(*(dsp+=waitfactor))-128;
f[29]=(*(dsp+=waitfactor))-128;
f[30]=(*(dsp+=waitfactor))-128;
f[31]=(*(dsp+=waitfactor))-128;
samped += waitfactor<<5;
// Calculate the integer FFT values
// for 16 frequency breaks.
f17m1 =f[17]-f[1]; f17p1 =f[17]+f[1];
f18m2 =f[18]-f[2]; f18p2 =f[18]+f[2];
f19m3 =f[19]-f[3]; f19p3 =f[19]+f[3];
f20m4 =f[20]-f[4]; f20p4 =f[20]+f[4];
f21m5 =f[21]-f[5]; f21p5 =f[21]+f[5];
f22m6 =f[22]-f[6]; f22p6 =f[22]+f[6];
f23m7 =f[23]-f[7]; f23p7 =f[23]+f[7];
f16p0 =f[16]+f[0]; f24p8 =f[24]+f[8];
f25m9 =f[25]-f[9]; f25p9 =f[25]+f[9];
f26m10=f[26]-f[10]; f26p10=f[26]+f[10];
f27m11=f[27]-f[11]; f27p11=f[27]+f[11];
f28m12=f[28]-f[12]; f28p12=f[28]+f[12];
f29m13=f[29]-f[13]; f29p13=f[29]+f[13];
f30m14=f[30]-f[14]; f30p14=f[30]+f[14];
f31m15=f[31]-f[15]; f31p15=f[31]+f[15];
f25m9Mf23m7 =f25m9 -f23m7; f25m9Pf23m7 =f25m9 +f23m7;
f26m10Mf22m6=f26m10-f22m6; f26m10Pf22m6=f26m10+f22m6;
f27m11Mf21m5=f27m11-f21m5; f27m11Pf21m5=f27m11+f21m5;
f29m13Mf19m3=f29m13-f19m3; f29m13Pf19m3=f29m13+f19m3;
f30m14Mf18m2=f30m14-f18m2; f30m14Pf18m2=f30m14+f18m2;
f31m15Mf17m1=f31m15-f17m1; f31m15Pf17m1=f31m15+f17m1;
f25p9Mf23p7 =f25p9 -f23p7; f25p9Pf23p7 =f25p9 +f23p7;
f26p10Mf22p6=f26p10-f22p6; f26p10Pf22p6=f26p10+f22p6;
f27p11Mf21p5=f27p11-f21p5; f27p11Pf21p5=f27p11+f21p5;
f24p8Pf16p0 =f24p8 +f16p0; f28p12Pf20p4=f28p12+f20p4;
f29p13Mf19p3=f29p13-f19p3; f29p13Pf19p3=f29p13+f19p3;
f30p14Mf18p2=f30p14-f18p2; f30p14Pf18p2=f30p14+f18p2;
f31p15Mf17p1=f31p15-f17p1; f31p15Pf17p1=f31p15+f17p1;
f29Mf19Pf27Mf21=f29p13Mf19p3+f27p11Mf21p5;
f29Pf19Mf27Pf21=f29p13Pf19p3-f27p11Pf21p5;
f29Mf19Mf27Mf21=f29p13Mf19p3-f27p11Mf21p5;
f31Mf17Pf25Mf23=f31p15Mf17p1+f25p9Mf23p7;
f31Pf17Mf25Pf23=f31p15Pf17p1-f25p9Pf23p7;
f31Mf17Mf25Mf23=f31p15Mf17p1-f25p9Mf23p7;
s4Mf30f18f26f22=S4*(f30p14Mf18p2+f26p10Mf22p6);
s4Pf30f26f22f18=S4*(f30p14Pf18p2-f26p10Pf22p6);
s4Mf31f29f27f25=S4*(f31Mf17Mf25Mf23+f29Mf19Mf27Mf21);
s4Pf31f29f27f25=S4*(f31p15Pf17p1+f25p9Pf23p7-f29p13Pf19p3-f27p11Pf21p5);
s8Mf30f18f26f22=S8*(f30p14Mf18p2-f26p10Mf22p6);
s8Pf28f24f20f16=S8*(f28p12Pf20p4-f24p8Pf16p0);
s8Mf24f16=S8*(f24p8-f16p0);
s8Mf28f20=S8*(f28p12-f20p4);
s4Mf30f26Ms8Mf28=s4Mf30f18f26f22-s8Mf28f20;
s4Mf30f26Ps8Mf28=s4Mf30f18f26f22+s8Mf28f20;
s4Pf30f22Ms8Mf24=s4Pf30f26f22f18-s8Mf24f16;
s4Pf30f22Ps8Mf24=s4Pf30f26f22f18+s8Mf24f16;
s6Mf31f25Ms2Mf29f27=S6*f31Mf17Pf25Mf23-S2*f29Mf19Pf27Mf21;
s6Mf29f27Ps2Mf31f25=S6*f29Mf19Pf27Mf21+S2*f31Mf17Pf25Mf23;
s6Pf29f27Ms2Pf31f25=S6*f29Pf19Mf27Pf21-S2*f31Pf17Mf25Pf23;
s6Pf31f25Ps2Pf29f27=S6*f31Pf17Mf25Pf23+S2*f29Pf19Mf27Pf21;
s1Mf25s3f27s5f29s7f31=S1*f25m9Mf23m7+S3*f27m11Mf21m5+S5*f29m13Mf19m3+S7*f31m15Mf17m1;
s1Pf25s3f27s5f29s7f31=S1*f25m9Pf23m7-S3*f27m11Pf21m5+S5*f29m13Pf19m3-S7*f31m15Pf17m1;
s1Mf27s3f31s5f25s7f29=S1*f27m11Mf21m5+S3*f31m15Mf17m1+S5*f25m9Mf23m7-S7*f29m13Mf19m3;
s1Pf27s3f31s5f25s7f29=S1*f27m11Pf21m5+S3*f31m15Pf17m1-S5*f25m9Pf23m7+S7*f29m13Pf19m3;
s1Mf29s3f25s5f31s7f27=S1*f29m13Mf19m3+S3*f25m9Mf23m7-S5*f31m15Mf17m1+S7*f27m11Mf21m5;
s1Pf29s3f25s5f31s7f27=S1*f29m13Pf19m3+S3*f25m9Pf23m7+S5*f31m15Pf17m1-S7*f27m11Pf21m5;
s1Mf31s3f29s5f27s7f25=S1*f31m15Mf17m1-S3*f29m13Mf19m3+S5*f27m11Mf21m5-S7*f25m9Mf23m7;
s1Pf31s3f29s5f27s7f25=S1*f31m15Pf17m1+S3*f29m13Pf19m3+S5*f27m11Pf21m5+S7*f25m9Pf23m7;
s2Mf26Ps6Mf30=S2*f26m10Mf22m6+S6*f30m14Mf18m2;;
s2Pf26Ms6Pf30=S2*f26m10Pf22m6-S6*f30m14Pf18m2;
s2Mf30Ms6Mf26=S2*f30m14Mf18m2-S6*f26m10Mf22m6;
s2Pf30Ps6Pf26=S2*f30m14Pf18m2+S6*f26m10Pf22m6;
s4Mf28f20=S4*(f28m12-f20m4); s4Pf28f20=S4*(f28m12+f20m4);
s8f16=S8*(f[16]-f[0]); s8f24=S8*(f[24]-f[8]);
s4Mf28Ps8f16=s4Mf28f20+s8f16;
s4Mf28Ms8f16=s4Mf28f20-s8f16;
s4Pf28Ps8f24=s4Pf28f20+s8f24;
s4Pf28Ms8f24=s4Pf28f20-s8f24;
s2Mf26Ps4f28s6f30Ms8f16=s2Mf26Ps6Mf30+s4Mf28Ms8f16;
s2Mf26Ms4f28s6f30Ps8f16=s2Mf26Ps6Mf30-s4Mf28Ms8f16;
s2Pf26Ms4f28s6f30Ps8f24=s2Pf26Ms6Pf30-s4Pf28Ms8f24;
s2Pf26Ps4f28s6f30Ms8f24=s2Pf26Ms6Pf30+s4Pf28Ms8f24;
s2Mf30Ps4f28s6f26Ps8f16=s2Mf30Ms6Mf26+s4Mf28Ps8f16;
s2Mf30Ms4f28s6f26Ms8f16=s2Mf30Ms6Mf26-s4Mf28Ps8f16;
s2Pf30Ps4f28s6f26Ps8f24=s2Pf30Ps6Pf26+s4Pf28Ps8f24;
s2Pf30Ms4f28s6f26Ms8f24=s2Pf30Ps6Pf26-s4Pf28Ps8f24;
// Copy the current average FFT
// values to a temporary buffer for
// erasing the display.
memcpy(oldFFT, avgFFT, sizeof(oldFFT));
// Subtract the oldest FFT result
// from the average total.
if (notTotReset)
{
totFFT[ 0] -= fft[0][ 0];
totFFT[ 1] -= fft[0][ 1];
totFFT[ 2] -= fft[0][ 2];
totFFT[ 3] -= fft[0][ 3];
totFFT[ 4] -= fft[0][ 4];
totFFT[ 5] -= fft[0][ 5];
totFFT[ 6] -= fft[0][ 6];
totFFT[ 7] -= fft[0][ 7];
totFFT[ 8] -= fft[0][ 8];
totFFT[ 9] -= fft[0][ 9];
totFFT[10] -= fft[0][10];
totFFT[11] -= fft[0][11];
totFFT[12] -= fft[0][12];
totFFT[13] -= fft[0][13];
totFFT[14] -= fft[0][14];
}
notTotReset = TRUE;
// Shift the FFT buffer left one
// graph, erasing the oldest FFT and
// making room for the new one.
memmove(&(fft[0][0]), &(fft[1][0]), N*(cntFFT-1));
// Calculate cntFFT minus 1 for speed
cntFFTm1 = cntFFT - 1;
// Fill in the resulting FFT array
// while adding the new values to the
// FFT total for "cntFFT" iterations.
// Also calculate the average FFT at
// this time.
avgFFT[ 0] =
(
totFFT[ 0] +=
(
fft[cntFFTm1][ 0] =
(abs(s1Mf25s3f27s5f29s7f31+s2Mf26Ps4f28s6f30Ms8f16)>>SCALE)+
(abs(s1Pf31s3f29s5f27s7f25+s2Pf30Ps4f28s6f26Ps8f24)>>SCALE)
)
) / cntFFT;
avgFFT[ 1] =
(
totFFT[ 1] +=
(
fft[cntFFTm1][ 1] =
(abs(s6Pf31f25Ps2Pf29f27+s4Pf30f22Ms8Mf24)>>SCALE)+
(abs(s6Mf29f27Ps2Mf31f25+s4Mf30f26Ps8Mf28)>>SCALE)
)
) / cntFFT;
avgFFT[ 2] =
(
totFFT[ 2] +=
(
fft[cntFFTm1][ 2] =
(abs(s2Mf30Ms4f28s6f26Ms8f16-s1Mf29s3f25s5f31s7f27)>>SCALE)+
(abs(s2Pf26Ms4f28s6f30Ps8f24-s1Pf27s3f31s5f25s7f29)>>SCALE)
)
) / cntFFT;
avgFFT[ 3] =
(
totFFT[ 3] +=
(
fft[cntFFTm1][ 3] =
(abs(s4Pf31f29f27f25-s8Pf28f24f20f16)>>SCALE)+
(abs(s4Mf31f29f27f25+s8Mf30f18f26f22)>>SCALE)
)
) / cntFFT;
avgFFT[ 4] =
(
totFFT[ 4] +=
(
fft[cntFFTm1][ 4] =
(abs(s1Mf27s3f31s5f25s7f29-s2Mf30Ps4f28s6f26Ps8f16)>>SCALE)+
(abs(s2Pf26Ps4f28s6f30Ms8f24-s1Pf29s3f25s5f31s7f27)>>SCALE)
)
) / cntFFT;
avgFFT[ 5] =
(
totFFT[ 5] +=
(
fft[cntFFTm1][ 5] =
(abs(s6Pf29f27Ms2Pf31f25+s4Pf30f22Ps8Mf24)>>SCALE)+
(abs(s6Mf31f25Ms2Mf29f27+s4Mf30f26Ms8Mf28)>>SCALE)
)
) / cntFFT;
avgFFT[ 6] =
(
totFFT[ 6] +=
(
fft[cntFFTm1][ 6] =
(abs(s1Mf31s3f29s5f27s7f25-s2Mf26Ms4f28s6f30Ps8f16)>>SCALE)+
(abs(s1Pf25s3f27s5f29s7f31-s2Pf30Ms4f28s6f26Ms8f24)>>SCALE)
)
) / cntFFT;
avgFFT[ 7] =
(
totFFT[ 7] +=
(
fft[cntFFTm1][ 7] =
(abs(S8*(f30p14Pf18p2+f26p10Pf22p6-f28p12Pf20p4-f24p8Pf16p0))>>SCALE)+
(abs(S8*(f31Mf17Mf25Mf23-f29Mf19Mf27Mf21))>>SCALE)
)
) / cntFFT;
avgFFT[ 8] =
(
totFFT[ 8] +=
(
fft[cntFFTm1][ 8] =
(abs(s1Mf31s3f29s5f27s7f25+s2Mf26Ms4f28s6f30Ps8f16)>>SCALE)+
(abs(s1Pf25s3f27s5f29s7f31+s2Pf30Ms4f28s6f26Ms8f24)>>SCALE)
)
) / cntFFT;
avgFFT[ 9] =
(
totFFT[ 9] +=
(
fft[cntFFTm1][ 9] =
(abs(s6Pf29f27Ms2Pf31f25-s4Pf30f22Ps8Mf24)>>SCALE)+
(abs(s4Mf30f26Ms8Mf28-s6Mf31f25Ms2Mf29f27)>>SCALE)
)
) / cntFFT;
avgFFT[10] =
(
totFFT[10] +=
(
fft[cntFFTm1][10] =
(abs(s1Mf27s3f31s5f25s7f29+s2Mf30Ps4f28s6f26Ps8f16)>>SCALE)+
(abs(s1Pf29s3f25s5f31s7f27+s2Pf26Ps4f28s6f30Ms8f24)>>SCALE)
)
) / cntFFT;
avgFFT[11] =
(
totFFT[11] +=
(
fft[cntFFTm1][11] =
(abs(s8Pf28f24f20f16+s4Pf31f29f27f25)>>SCALE)+
(abs(s8Mf30f18f26f22-s4Mf31f29f27f25)>>SCALE)
)
) / cntFFT;
avgFFT[12] =
(
totFFT[12] +=
(
fft[cntFFTm1][12] =
(abs(s1Mf29s3f25s5f31s7f27+s2Mf30Ms4f28s6f26Ms8f16)>>SCALE)+
(abs(s1Pf27s3f31s5f25s7f29+s2Pf26Ms4f28s6f30Ps8f24)>>SCALE)
)
) / cntFFT;
avgFFT[13] =
(
totFFT[13] +=
(
fft[cntFFTm1][13] =
(abs(s4Pf30f22Ms8Mf24-s6Pf31f25Ps2Pf29f27)>>SCALE)+
(abs(s4Mf30f26Ps8Mf28-s6Mf29f27Ps2Mf31f25)>>SCALE)
)
) / cntFFT;
avgFFT[14] =
(
totFFT[14] +=
(
fft[cntFFTm1][14] =
(abs(s2Mf26Ps4f28s6f30Ms8f16-s1Mf25s3f27s5f29s7f31)>>SCALE)+
(abs(s2Pf30Ps4f28s6f26Ps8f24-s1Pf31s3f29s5f27s7f25)>>SCALE)
)
) / cntFFT;
/*
// Provide a filter to drop out low
// signals.
if (avgFFT[ 0]<offset) avgFFT[ 0] = 0;
if (avgFFT[ 1]<offset) avgFFT[ 1] = 0;
if (avgFFT[ 2]<offset) avgFFT[ 2] = 0;
if (avgFFT[ 3]<offset) avgFFT[ 3] = 0;
if (avgFFT[ 4]<offset) avgFFT[ 4] = 0;
if (avgFFT[ 5]<offset) avgFFT[ 5] = 0;
if (avgFFT[ 6]<offset) avgFFT[ 6] = 0;
if (avgFFT[ 7]<offset) avgFFT[ 7] = 0;
if (avgFFT[ 8]<offset) avgFFT[ 8] = 0;
if (avgFFT[ 9]<offset) avgFFT[ 9] = 0;
if (avgFFT[10]<offset) avgFFT[10] = 0;
if (avgFFT[11]<offset) avgFFT[11] = 0;
if (avgFFT[12]<offset) avgFFT[12] = 0;
if (avgFFT[13]<offset) avgFFT[13] = 0;
if (avgFFT[14]<offset) avgFFT[14] = 0;
*/
// Display the smoothed FFT to the
// on-screen audio spectrum analyzer.
for (l=0,line=0; l<(N-1); l++,line+=160)
{
level = avgFFT[l];
oldlevel = oldFFT[l];
// Display the signal strength for
// this frequency.
for (k=1,column=2; k<=level; k++,column+=2)
{
pokeb(VIDSEG,80+line+column,0xFE);
}
// Erase any left-overs for the
// previous sample.
for (k=level+1; k<=oldlevel; k++,column+=2)
{
pokeb(VIDSEG,80+line+column,0xFA);
}
// Display the max-strength level for
// this frequency, if it's changed.
if ((level)&&((level+1) >= maxlevel[l]))
{
maxlevel[l] = level+1;
pokeb(VIDSEG,80+line+(maxlevel[l]<<1),0x07);
}
}
// Tell the calling function how many
// samples this function has used.
return(samped);
}
/////////////////////////////////////////////////////////////////////////////
//
// Convert dits and dahs to text. The way it's done wasn't immediately
// apparent to me, so I'll explain: Morse dots are added as 1 bits, right
// to left in the code value. Eventually, when a space is detected, the
// value in code is the reverse of the morse letter. Since it's a single
// value, it's easily decoded into text. Very smart!
// To do: Convert the switch statement to a look-up table.
//
// Add a "dot" to the morse code being built.
//
void doDot()
{
morsePutC(0xF9);
code += mask;
mask <<= 1;
pending = TRUE;
}
//
// Add a "dash" to the morse code being built.
//
void doDash()
{
morsePutC('-');
mask <<= 1;
pending = TRUE;
}
//
// Space is detected between morse codes, so translate what was built into
// a letter and display it.
//
void doSpace()
{
char c;
if (pending)
{
code += mask;
switch (code)
{
case 2: c='T'; break; // -
case 3: c='E'; break; // -
case 4: c='M'; break; // --
case 5: c='A'; break; // ∙-
case 6: c='N'; break; // -∙
case 7: c='I'; break; // ∙∙
case 8: c='O'; break; // ---
case 9: c='W'; break; // ∙--
case 10: c='K'; break; // -∙-
case 11: c='U'; break; // ∙∙-
case 12: c='G'; break; // --∙
case 13: c='R'; break; // ∙-∙
case 14: c='D'; break; // -∙∙
case 15: c='S'; break; // ∙∙∙
case 17: c='J'; break; // ∙---
case 18: c='Y'; break; // -∙--
case 19: c='Ü'; break; // ∙∙--
case 20: c='Q'; break; // --∙-
case 21: c='Ä'; break; // ∙-∙-
case 22: c='X'; break; // -∙∙-
case 23: c='V'; break; // ∙∙∙-
case 24: c='Ö'; break; // ---∙
case 25: c='P'; break; // ∙--∙
case 26: c='C'; break; // -∙-∙
case 27: c='F'; break; // ∙∙-∙
case 28: c='Z'; break; // --∙∙
case 29: c='L'; break; // ∙-∙∙
case 30: c='B'; break; // -∙∙∙
case 31: c='H'; break; // ∙∙∙∙
case 32: c='0'; break; // -----
case 33: c='1'; break; // ∙----
case 35: c='2'; break; // ∙∙---
case 36: c='Ñ'; break; // --∙--
case 39: c='3'; break; // ∙∙∙--
case 41: c='Å'; break; // ∙--∙-
case 47: c='4'; break; // ∙∙∙∙-
case 48: c='9'; break; // ----∙
case 54: c='/'; break; //
case 56: c='8'; break; // ---∙∙
case 59: c='É'; break; // ∙∙-∙∙
case 60: c='7'; break; // --∙∙∙
case 62: c='6'; break; // -∙∙∙∙
case 63: c='5'; break; // ∙∙∙∙∙
case 76: c=','; break; //
case 82: c='|'; break; //
case 85: c='.'; break; //
case 94: c='-'; break; //
case 106: c=';'; break; //
case 115: c='?'; break; //
case 120: c=':'; break; //
default:
c=0xFE;
morsePutC(0xFE);
break;
}
morsePutC(0x20);
textPutC(c);
if (thisCnt >= (useSpace*3))
{
textPutC(0x20);
}
code=0;
mask=1;
}
pending=FALSE;
}
/////////////////////////////////////////////////////////////////////////////
//
// Determine if a mark is present in the FFT window.
// To do: Allow for 1-row, 3-row and 5-row windows.
void FFT2tone(void)
{
static int target=0;
static int foctot=0, focavg=0;
static int remavg=0;
static int alltot=0, allavg=0;
static int oldremavg=0, oldfocavg=0, oldallavg=0;
oldfocavg = focavg;
focavg= (
(
foctot= (
avgFFT[freqm2]+
avgFFT[freqm1]+
avgFFT[freq]+
avgFFT[freqp1]+
avgFFT[freqp2]
)
) << 1
) / 5;
oldallavg = allavg;
alltot= (
avgFFT[ 0]+
avgFFT[ 1]+
avgFFT[ 2]+
avgFFT[ 3]+
avgFFT[ 4]+
avgFFT[ 5]+
avgFFT[ 6]+
avgFFT[ 7]+
avgFFT[ 8]+
avgFFT[ 9]+
avgFFT[10]+
avgFFT[11]+
avgFFT[12]+
avgFFT[13]+
avgFFT[14]
);
if (tuneout)
{
alltot -= (
avgFFT[outm1]+
avgFFT[out]+
avgFFT[outp1]
);
}
allavg = (alltot<<1) / (N-1);
oldremavg = remavg;
remavg = ((alltot-foctot)<<1) / ((N-1)-5);
// Erase old averages display.
pokeb(VIDSEG, 2482+(oldfocavg<<1), 0x20);
pokeb(VIDSEG, 2482+(oldallavg<<1), 0x20);
pokeb(VIDSEG, 2482+(oldremavg<<1), 0x20);
pokeb(VIDSEG, 2482+(target<<1), 0x20);
pokeb(VIDSEG, 2482+(allavg<<1), 0x1E);
pokeb(VIDSEG, 2482+(remavg<<1), 0x1F);
pokeb(VIDSEG, 2482+(focavg<<1), 0x18);
// Calculate target signal level.
// If the focus average is above this
// target, a tone is considered to be
// present.
target = ((allavg+remavg)>>1)+offset;
pokeb(VIDSEG, 2482+(target<<1), 0xB3);
// Determine if a tone is present.
// Adds optional audio feedback, too.
oldtonedetected = tonedetected;
if ((focavg) > (target))
{
tonedetected=TRUE;
if (noise)
{
sound(880);
}
tonePutC(0xFE);
}
else
{
tonedetected=FALSE;
nosound();
tonePutC(0x20);
}
// Edge-detect mark-to-space and
// space-to-mark transitions.
if (oldtonedetected != tonedetected)
{
if (thisCnt>=thrNoise)
{
if (tonedetected)
{
if ((totSpace+thisCnt)>totSpace)
{
if (notSpaceReset)
{
totSpace -= bufSpace[0];
}
notSpaceReset = TRUE;
memmove(bufSpace, bufSpace+1, (cntBuf-1)*sizeof(bufSpace[0]));
avgSpace = (totSpace += (bufSpace[cntBuf-1] = thisCnt))/cntBuf;
if (thisCnt >= useSpace)
{
doSpace();
}
}
}
else
{
if ((totMark+thisCnt)>totMark)
{
if (notMarkReset)
{
totMark -= bufMark[0];
}
notMarkReset = TRUE;
memmove(bufMark, bufMark+1, (cntBuf-1)*sizeof(bufSpace[0]));
avgMark = (totMark += (bufMark[cntBuf-1] = thisCnt))/cntBuf;
if (thisCnt >= useMark)
{
doDash();
}
else
{
doDot();
}
}
}
thisCnt=0;
}
}
else
{
if (!tonedetected)
{
if (pending && (thisCnt>(useSpace*3)))
{
doSpace();
notSpaceReset = TRUE;
memmove(bufSpace, bufSpace+1, (cntBuf-1)*sizeof(bufSpace[0]));
avgSpace = (totSpace += (bufSpace[cntBuf-1] = thisCnt))/cntBuf;
thisCnt=0;
}
}
}
thisCnt+=1;
}
/////////////////////////////////////////////////////////////////////////////
void updateCursors(char onoff)
{
char c1, c2;
int c, col;
// Determine the cursor characters.
if (onoff)
{
c1 = '<';
c2 = '>';
}
else
{
c1 = c2 = ' ';
}
// Calculate the focus cursor range.
freqm2 = freq - 2;
freqm1 = freq - 1;
freqp1 = freq + 1;
freqp2 = freq + 2;
// Calculate the tuned-out cursor.
outm1 = out - 1;
outp1 = out + 1;
// Display the tuned-out cursor.
pokeb(VIDSEG,(74)+(160*outm1), c1);
pokeb(VIDSEG,(74)+(160*out), c1);
pokeb(VIDSEG,(74)+(160*outp1), c1);
// Tune out that graph portion.
for (c=41; c<GRAPHTOP; c++)
{
if (tuneout && onoff)
{
col=8;
}
else
{
if (c<BLU) col=9;
else if (c<GRN) col=10;
else if (c<YEL) col=14;
else col=12;
}
pokeb(VIDSEG,((outm1)*160)+(c<<1)+1,col);
pokeb(VIDSEG,((out)*160)+(c<<1)+1,col);
pokeb(VIDSEG,((outp1)*160)+(c<<1)+1,col);
}
// Display the frequency cursor.
pokeb(VIDSEG,(76)+(160*freqm2),c2);
pokeb(VIDSEG,(76)+(160*freqm1),c2);
pokeb(VIDSEG,(76)+(160*freq),c2);
pokeb(VIDSEG,(76)+(160*freqp1),c2);
pokeb(VIDSEG,(76)+(160*freqp2),c2);
// Focus on the graph bit.
for (c=41; c<GRAPHTOP; c++)
{
if (c<BLU) col=9;
else if (c<GRN) col=10;
else if (c<YEL) col=14;
else col=12;
if (onoff)
{
col |= 0x68;
}
pokeb(VIDSEG,((freqm2)*160)+(c<<1)+1,col);
pokeb(VIDSEG,((freqm1)*160)+(c<<1)+1,col);
pokeb(VIDSEG,((freq)*160)+(c<<1)+1,col);
pokeb(VIDSEG,((freqp1)*160)+(c<<1)+1,col);
pokeb(VIDSEG,((freqp2)*160)+(c<<1)+1,col);
}
}
/////////////////////////////////////////////////////////////////////////////
void clearGraph(void)
{
int l, l160, c;
char col;
// Clear the audio spectrum analyzer
// on the screen.
for (l=0,l160=0; l<(N-1); l++,l160+=160)
{
for (c=41; c<GRAPHTOP; c++)
{
if (c<BLU) col=9; // 9
else if (c<GRN) col=10; // 10
else if (c<YEL) col=14; // 14
else col=12; // 12
pokeb(VIDSEG,l160+(c<<1),0xFA);
pokeb(VIDSEG,l160+(c<<1)+1,col);
}
}
// Squonk the old and average FFT
// values, since these will change.
memset(oldFFT, 0, sizeof(oldFFT));
memset(totFFT, 0, sizeof(totFFT));
memset(fft, 0, sizeof(fft));
memset(maxlevel, 0, sizeof(maxlevel));
// Reset the total FFT buffer too.
notTotReset = FALSE;
}
#include <bios.h>
#define UPKEY 0x4800
#define SUPKEY 0x4838
#define SFIVEKEY 0x4C35
#define DOWNKEY 0x5000
#define SDOWNKEY 0x5032
#define ESCKEY 0x011B
#define PLUSKEY 0x4E2B
#define PLUSKEY2 0x0D2B
#define MINUSKEY 0x4A2D
#define MINUSKEY2 0x0C2D
#define SPACEKEY 0x3920
#define STARKEY 0x372A
#define STARKEY2 0x092A
#define LEFTKEY 0x4B00
#define RIGHTKEY 0x4D00
/*
3 4 5 6 7
89-123456789-123456789-123456789-123456789
17 1|(QW) Freq: 12345 * ---- Overruns: 33333
18 2|(AS) Skip: 123 (ER) Smooth : 333
19 3|(IO) Mark: 1234/1234 (DF) SmMorse : 333
20 4|(KL) Spc : 1234/1234 (--) Offset : 333
21 5|(ZX) Nois: 1234
22 7|* toggles freq autoadjust. (T) Tuneout *
23 8|| and | focus. ESC exits. SHIFT+| & |
24 9|SPACE toggles audio (xxx). move tuneout
----------------------------------------------- tone display line
*/
void updateDisplay(void)
{
gotoxy(38,17); cprintf("(QW) Freq: %-5u %c ---- Overruns: %-5d", sbSampleRate, ((autorate)?('*'):(' ')), sbOver);
gotoxy(38,18); cprintf("(AS) Skip: %-3d (ER) Smooth : %-3d ", waitfactor, cntFFT);
gotoxy(38,19); cprintf("(IO) Mark: %-4u/%-4u (DF) SmoMorse: %-3d", avgMark, useMark, cntBuf);
gotoxy(38,20); cprintf("(KL) Spc : %-4u/%-4u (%c%c) Offset : %3d ", avgSpace, useSpace, 0x1B, 0x1A, offset);
gotoxy(38,21); cprintf("(ZX) Nois: %-4u", thrNoise);
gotoxy(38,22); cprintf("* toggles freq autoadjust. (T) Tuneout %c", ((tuneout)?('*'):(' ')));
gotoxy(38,23); cprintf("%c and %c focus. ESC exits. SHIFT+%c & %c", 0x18, 0x19, 0x18, 0x19);
// gotoxy(38,24); cprintf("SPACE toggles audio (%s move tuneout", ((noise)?("on). "):("off).")));
}
void clearMorse(void)
{
totMark = avgMark = 0;
totSpace = avgSpace = 0;
notMarkReset = FALSE;
notSpaceReset = FALSE;
memset(bufMark, 0, sizeof(bufMark));
memset(bufSpace, 0, sizeof(bufSpace));
memset(maxlevel, 0, sizeof(maxlevel));
updateDisplay();
}
void handlekey(char *done)
{
int bk,l,c,col;
updateCursors(FALSE);
bk = bioskey(0);
switch (bk)
{
case SUPKEY:
if (out>1) out--;
break;
case SDOWNKEY:
if (out<(N-3)) out++;
break;
case UPKEY:
if (freq>2) freq--;
break;
case DOWNKEY:
if (freq<(N-4)) freq++;
break;
case LEFTKEY:
if (offset>0) offset--;
break;
case RIGHTKEY:
if (offset<32) offset++;
break;
case ESCKEY:
*done=TRUE;
break;
default:
switch (bk&0xFF)
{
case 'q':
case 'Q':
if (sbSampleRate>SAMPLEMIN)
{
sbSampleRate -= SAMPLEINC;
updateDisplay();
}
break;
case 'w':
case 'W':
if (sbSampleRate<SAMPLEMAX)
{
sbSampleRate += SAMPLEINC;
updateDisplay();
}
break;
case 'e':
case 'E':
if (cntFFT>1)
{
cntFFT--;
updateDisplay();
clearGraph();
}
break;
case 'r':
case 'R':
if (cntFFT<MAXFFT)
{
cntFFT++;
updateDisplay();
clearGraph();
}
break;
case 'a':
case 'A':
if (waitfactor>1)
{
waitfactor--;
updateDisplay();
}
break;
case 's':
case 'S':
if (waitfactor<128)
{
waitfactor++;
updateDisplay();
}
break;
case ' ':
noise = !noise;
if (!noise) nosound();
updateDisplay();
break;
case '8':
case '*':
autorate = !autorate;
updateDisplay();
break;
case 'd':
case 'D':
if (cntBuf>MINBUF)
{
cntBuf--;
clearMorse();
}
break;
case 'f':
case 'F':
if (cntBuf<MAXBUF)
{
cntBuf++;
clearMorse();
}
break;
case 'i':
if (useMark>1)
{
useMark--;
updateDisplay();
}
break;
case 'I':
if (useMark>100)
{
useMark-=100;
updateDisplay();
}
break;
case 'o':
if (useMark<9999)
{
useMark++;
updateDisplay();
}
break;
case 'O':
if (useMark<9899)
{
useMark+=100;
updateDisplay();
}
break;
case 'k':
if (useSpace>1)
{
useSpace--;
updateDisplay();
}
break;
case 'K':
if (useSpace>100)
{
useSpace -= 100;
updateDisplay();
}
break;
case 'l':
if (useSpace<9999)
{
useSpace++;
updateDisplay();
}
break;
case 'L':
if (useSpace<9899)
{
useSpace+=100;
updateDisplay();
}
break;
case 'z':
if (thrNoise>1)
{
thrNoise--;
updateDisplay();
}
break;
case 'Z':
if (thrNoise>100)
{
thrNoise-=100;
updateDisplay();
}
break;
case 'x':
if (thrNoise<9999)
{
thrNoise++;
updateDisplay();
}
break;
case 'X':
if (thrNoise<9899)
{
thrNoise+=100;
updateDisplay();
}
break;
case 't':
case 'T':
tuneout = !tuneout;
updateDisplay();
break;
}
}
updateCursors(TRUE);
}
/*------------------------------- Initialize ---------------------------------*/
//
// Initialize the screen, data and hardware.
//
void begin(void)
{
static int l,line,xx,yy;
// Set up the Sound Blaster, and
// display (although briefly) some
// interesting information about it.
sbOpen();
printf("\nSetStat: %d", sbSetStatWord(&sbStat));
printf("\nRecSrc : %d", sbSetRecSrc(3)); // line in
printf("\nRecMode: %d", sbSetRecMode(0)); // mono recording
printf("\nSpkOnOf: %d", sbSpeaker(1)); // speaker goes on
// Clear the screen. Set up static
// text and color regions.
clrscr();
clearGraph();
// Display box horizontal lines
for (xx=1; xx<35; xx++)
{
pokeb(VIDSEG,(xx<<1)+(160*BOXTOP),0xC4);
pokeb(VIDSEG,(xx<<1)+(160*BOXDIV),0xC4);
pokeb(VIDSEG,(xx<<1)+(160*BOXBOT),0xC4);
}
// Display box vertical lines
for (yy=1; yy<23; yy++)
{
pokeb(VIDSEG,(160*yy)+(BOXLEF<<1),0xB3);
pokeb(VIDSEG,(160*yy)+(BOXRIG<<1),0xB3);
}
// Display box & divide corners
pokeb(VIDSEG,(160*BOXTOP)+(BOXLEF<<1),0xDA);
pokeb(VIDSEG,(160*BOXTOP)+(BOXRIG<<1),0xBF);
pokeb(VIDSEG,(160*BOXDIV)+(BOXLEF<<1),0xC3);
pokeb(VIDSEG,(160*BOXDIV)+(BOXRIG<<1),0xB4);
pokeb(VIDSEG,(160*BOXBOT)+(BOXLEF<<1),0xC0);
pokeb(VIDSEG,(160*BOXBOT)+(BOXRIG<<1),0xD9);
updateCursors(TRUE);
// Display the left base for the
// on-screen audio spectrum analyzer.
for (l=0,line=0; l<(N-1); l++,line+=160)
{
pokeb(VIDSEG,80+line,'≡');
}
// Allocate room for the input and
// analysis sample buffers. Make
// sure they start at offset 0000.
// Allocate 64 extra bytes, 16 for
// zero-offset, and 48 for breathing
// space.
bufone = (char far *)farmalloc(SAMPLELEN+0x40);
buftwo = (char far *)farmalloc(SAMPLELEN+0x40);
if ((bufone==NULL) || (buftwo==NULL))
{
puts("Woops! No room for buffers!");
exit(1);
}
bufone = MK_FP(FP_SEG(bufone)+1,0);
buftwo = MK_FP(FP_SEG(buftwo)+1,0);
// Display initial control panel.
updateDisplay();
}
/////////////////////////////////////////////////////////////////////////////
//
// Mainline function.
//
#pragma argsused
int main(int argc, char **argv)
{
int rv, l, line;
UINT sampBytes=0;
begin();
while (!done)
{
// swap input/dsp buffers for DMA
if (bufin==bufone)
{
bufin = buftwo;
bufdsp = bufone;
}
else
{
bufin = bufone;
bufdsp = buftwo;
}
// record to bufin
if (sbInVoc(sbSampleRate, bufin, SAMPLELEN))
{
puts("Error recording.");
goto quickdone;
}
// Process the DSP buffer from last read. Skip the 16-byte block
// header that CT-VOICE.DRV uses to describe the block
dspcount = 16;
bufdsp += 16;
while (dspcount<SAMPLELEN)
{
dspcount += FFT(bufdsp);
FFT2tone();
}
sampBytes += SAMPLELEN;
// Sample bytes tested >= 1 sec worth
if (sampBytes >= sbSampleRate)
{
sampBytes = 0;
for (l=0,line=0; l<(N-1); l++,line+=160)
{
if (maxlevel[l]>1)
{
pokeb(VIDSEG,80+line+((maxlevel[l])*2),0xFA);
maxlevel[l]--;
pokeb(VIDSEG,80+line+((maxlevel[l])*2),0x07);
}
else
{
pokeb(VIDSEG,82+line,0xFA);
}
}
}
// handle keypresses
if (kbhit())
{
handlekey(&done);
if (done) goto quickdone;
}
// eat time until record finishes
if (sbStat)
{
// Update display only if is time
updateDisplay();
while (sbStat)
{
if (kbhit())
{
handlekey(&done);
if (done) goto quickdone;
}
idlePutC(0xF0);
}
}
else
{
if (autorate)
{
if (sbSampleRate>SAMPLEMIN)
{
sbSampleRate-=SAMPLEINC;
}
}
if (sbOver<0xFFFF) sbOver++;
updateDisplay();
idlePutC(0xEC);
}
idlePutC(0x20);
}
while (kbhit()) getch();
quickdone:
gotoxy(1,24);
nosound();
sbSpeaker(0); // turn speaker back on
sbClose();
return(0);
}